use actix_web::{HttpResponse, ResponseError};
use serde::{Deserialize, Serialize};
use std::fmt;
use thiserror::Error;

pub type AppResult<T> = Result<T, AppError>;

#[derive(Error, Debug)]
pub enum AppError {
    #[error("Database error: {0}")]
    Database(#[from] sea_orm::DbErr),
    
    #[error("Configuration error: {0}")]
    Config(#[from] config::ConfigError),
    
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),
    
    #[error("Validation error: {0}")]
    Validation(String),
    
    #[error("Authentication error: {0}")]
    Authentication(String),
    
    #[error("Authorization error: {0}")]
    Authorization(String),
    
    #[error("Not found: {0}")]
    NotFound(String),
    
    #[error("Bad request: {0}")]
    BadRequest(String),
    
    #[error("Internal server error: {0}")]
    Internal(String),
    
    #[error("JWT error: {0}")]
    Jwt(#[from] jsonwebtoken::errors::Error),
    
    #[error("Serialization error: {0}")]
    Serialization(#[from] serde_json::Error),
    
    #[error("HTTP client error: {0}")]
    HttpClient(#[from] reqwest::Error),
    
    #[error("Password hashing error: {0}")]
    PasswordHash(String),
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ErrorResponse {
    pub error: String,
    pub message: String,
    pub code: u16,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub details: Option<serde_json::Value>,
}

impl ErrorResponse {
    pub fn new(error: &str, message: &str, code: u16) -> Self {
        Self {
            error: error.to_string(),
            message: message.to_string(),
            code,
            details: None,
        }
    }
    
    pub fn with_details(mut self, details: serde_json::Value) -> Self {
        self.details = Some(details);
        self
    }
}

impl ResponseError for AppError {
    fn error_response(&self) -> HttpResponse {
        let (status, error_type, message) = match self {
            AppError::Database(e) => {
                tracing::error!("Database error: {}", e);
                (actix_web::http::StatusCode::INTERNAL_SERVER_ERROR, "DATABASE_ERROR", "Database operation failed")
            }
            AppError::Config(e) => {
                tracing::error!("Configuration error: {}", e);
                (actix_web::http::StatusCode::INTERNAL_SERVER_ERROR, "CONFIG_ERROR", "Configuration error")
            }
            AppError::Io(e) => {
                tracing::error!("IO error: {}", e);
                (actix_web::http::StatusCode::INTERNAL_SERVER_ERROR, "IO_ERROR", "IO operation failed")
            }
            AppError::Validation(msg) => {
                (actix_web::http::StatusCode::BAD_REQUEST, "VALIDATION_ERROR", msg.as_str())
            }
            AppError::Authentication(msg) => {
                (actix_web::http::StatusCode::UNAUTHORIZED, "AUTHENTICATION_ERROR", msg.as_str())
            }
            AppError::Authorization(msg) => {
                (actix_web::http::StatusCode::FORBIDDEN, "AUTHORIZATION_ERROR", msg.as_str())
            }
            AppError::NotFound(msg) => {
                (actix_web::http::StatusCode::NOT_FOUND, "NOT_FOUND", msg.as_str())
            }
            AppError::BadRequest(msg) => {
                (actix_web::http::StatusCode::BAD_REQUEST, "BAD_REQUEST", msg.as_str())
            }
            AppError::Internal(msg) => {
                tracing::error!("Internal error: {}", msg);
                (actix_web::http::StatusCode::INTERNAL_SERVER_ERROR, "INTERNAL_ERROR", "Internal server error")
            }
            AppError::Jwt(e) => {
                tracing::warn!("JWT error: {}", e);
                (actix_web::http::StatusCode::UNAUTHORIZED, "JWT_ERROR", "Invalid token")
            }
            AppError::Serialization(e) => {
                tracing::error!("Serialization error: {}", e);
                (actix_web::http::StatusCode::INTERNAL_SERVER_ERROR, "SERIALIZATION_ERROR", "Serialization failed")
            }
            AppError::HttpClient(e) => {
                tracing::error!("HTTP client error: {}", e);
                (actix_web::http::StatusCode::BAD_GATEWAY, "HTTP_CLIENT_ERROR", "External service error")
            }
            AppError::PasswordHash(e) => {
                tracing::error!("Password hashing error: {}", e);
                (actix_web::http::StatusCode::INTERNAL_SERVER_ERROR, "PASSWORD_HASH_ERROR", "Password processing failed")
            }
        };

        let error_response = ErrorResponse::new(error_type, message, status.as_u16());
        
        HttpResponse::build(status).json(error_response)
    }
}

// Helper functions for common errors
impl AppError {
    pub fn validation<T: fmt::Display>(msg: T) -> Self {
        Self::Validation(msg.to_string())
    }
    
    pub fn not_found<T: fmt::Display>(msg: T) -> Self {
        Self::NotFound(msg.to_string())
    }
    
    pub fn bad_request<T: fmt::Display>(msg: T) -> Self {
        Self::BadRequest(msg.to_string())
    }
    
    pub fn unauthorized<T: fmt::Display>(msg: T) -> Self {
        Self::Authentication(msg.to_string())
    }
    
    pub fn forbidden<T: fmt::Display>(msg: T) -> Self {
        Self::Authorization(msg.to_string())
    }
    
    pub fn internal<T: fmt::Display>(msg: T) -> Self {
        Self::Internal(msg.to_string())
    }
}